/* 目的:该例程为NodeMCU连接阿里云Iot平台的例程
 * 作者:Zeeland
 * 最后修改时间:2021年12月2日 18:33:56
 * https://gitee.com/zeeland/projects
 */


#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <ESP8266WiFiMulti.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "ntp1.aliyun.com",60*60*8, 30*60*1000);
String currentTime;

/* 设备证书信息*/
#define PRODUCT_KEY       "gmvzwtDHyC6"
#define DEVICE_NAME       "Instance1"
#define DEVICE_SECRET     "26198b62d8b0763516bb05af34b96574"
#define REGION_ID         "cn-shanghai"

/* Aliyun线上环境域名和端口号，不需要改 */
#define MQTT_SERVER       PRODUCT_KEY ".iot-as-mqtt." REGION_ID ".aliyuncs.com"
#define MQTT_PORT         1883
#define MQTT_USRNAME      DEVICE_NAME "&" PRODUCT_KEY

#define CLIENT_ID         "gmvzwtDHyC6.Instance1|securemode=2,signmethod=hmacsha256,timestamp=2524608000000|"
// MQTT连接报文参数,请参见MQTT-TCP连接通信文档，文档地址：https://help.aliyun.com/document_detail/73742.html
// 加密明文是参数和对应的值（clientIdesp8266deviceName${deviceName}productKey${productKey}timestamp1234567890）按字典顺序拼接
// 密钥是设备的DeviceSecret
//要使用加密工具，输入以上证书信息加密（时间戳可以省略）
#define MQTT_PASSWD       "a29f727768b1361f9073e199ab6e37ee0e3e75f1320d0219ad0a204b5bbb1420"

// 发送报文的json格式
#define ALINK_BODY_FORMAT         "{\"id\":\"123\",\"version\":\"1.0\",\"method\":\"thing.event.property.post\",\"params\":%s}"
// 上报报文主题
#define ALINK_TOPIC_PROP_POST     "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/event/property/post"

unsigned long lastMs = 0;
WiFiClient espClient;
PubSubClient  client(espClient);
ESP8266WiFiMulti wifiMulti;

unsigned int pwm_r=0,pwm_g=0,pwm_b=0;
int openFlag = 0;


void setup() 
{   
    Serial.begin(9600);
    Serial.println("[info] Demo Start");
    Serial.print("[info] CLIENT_ID:");Serial.println(CLIENT_ID);  
    Serial.print("[info] MQTT_USRNAME:");Serial.println(MQTT_USRNAME);
    Serial.print("[info] MQTT_PASSWD:");Serial.println(MQTT_PASSWD);

    // 连接WIFI
    wifiInit();

    // 初始化Mqtt服务
    mqttServeInit();

    // 初始化NTP时间服务
    timeClient.begin();
    
}



void loop()
{
    //millis()是系统启动到目前的总时间，以下为5s上传一次数据
    if (millis() - lastMs >= 3000)
    {
        // 获取当前时间
        lastMs = millis();
        
        // 检查连接状态
        mqttCheckConnect(); 

        timeClient.update();
        // 获取当前时间
        currentTime = timeClient.getFormattedTime();
        Serial.print("[info] now time is :");
        Serial.println(currentTime);
        
        // 上报消息
        mqttIntervalPost();

        // 根据下发的数据进行反馈
        work();
        
    }
    client.loop();
}

void work() {
  if(openFlag ==1){
    Serial.println("[info] this is the truly answer!!!");
  }
}

// 初始化Mqtt服务
void mqttServeInit() {
    // 设置MQTT服务器和端口号  
    client.setServer(MQTT_SERVER, MQTT_PORT);  
    // 设置MQTT订阅回调函数
    client.setCallback(callback);
}

// 收到信息后的回调函数
void callback(char *topic, byte *payload, unsigned int length)
{
    Serial.print("[info]Message arrived，the topic is [");
    Serial.print(topic);
    Serial.println("] ");
    payload[length] = '\0';

    const char* json = (char *)payload;
    Serial.println("收到的json：");
    Serial.println(json);
    DynamicJsonDocument doc(1024);
    deserializeJson(doc, json);
    JsonObject root = doc.as<JsonObject>();

    //云端下发的数据只有一个数据点，因此要判断是哪一个数据点下发了数据
    if( root["params"].containsKey("openDoor") )  //containsKey方法为判断json对象是否包含指定字段
    {  
       openFlag = root["params"]["openDoor"];
    }
    

}

// 连接wifi
void wifiInit()
{
  wifiMulti.addAP("LAPTOP-RIH1JO89 5592", "12345678"); // 将需要连接的一系列WiFi ID和密码输入这里
  wifiMulti.addAP("MI 9", "12345678"); // ESP8266-NodeMCU再启动后会扫描当前网络
  wifiMulti.addAP("LAPTOP9#337", "xy1229033519"); // 环境查找是否有这里列出的WiFi ID。如果有
  Serial.println("[info] Connecting ...");                            // 则尝试使用此处存储的密码进行连接。
  
  int i = 0;                                 
  while (wifiMulti.run() != WL_CONNECTED) {  // 此处的wifiMulti.run()是重点。通过wifiMulti.run()，NodeMCU将会在当前
    delay(1000);                             // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
    Serial.print(i++); Serial.print(' ');    // 将会连接信号最强的那一个WiFi信号。
  }                                          // 一旦连接WiFI成功，wifiMulti.run()将会返回“WL_CONNECTED”。这也是
                                             // 此处while循环判断是否跳出循环的条件。

  // WiFi连接成功后将通过串口监视器输出连接成功信息 
  Serial.print("[info] Connected to ");
  Serial.println(WiFi.SSID());              // 通过串口监视器输出连接的WiFi名称
  Serial.print("[info] IP address:\t");
  Serial.println(WiFi.localIP());           // 通过串口监视器输出ESP8266-NodeMCU的IP

}

// 检查设备与MQTT服务器连接情况
void mqttCheckConnect()
{
    while (!client.connected())
    {
        Serial.println("Connecting to MQTT Server ...");
        if (client.connect(CLIENT_ID, MQTT_USRNAME, MQTT_PASSWD))
        {
            Serial.println("MQTT Connected!");
        }
        else
        {
            Serial.print("MQTT Connect err:");
            Serial.println(client.state());
            delay(5000);
        }
    }
    if (client.connected()){
      Serial.println("[info] keeping alive");
    }
}

/* 上报消息 */
void mqttIntervalPost()
{
    char param[128];
    char jsonBuf[128];

    //上传的数据在这里编辑，格式如下
    const char * temp = currentTime.c_str();// 将current转换为字符数组
    sprintf(param, "{\"openTime\":\"%s\"}",temp);
    
    sprintf(jsonBuf, ALINK_BODY_FORMAT, param);
    Serial.println("[info] 上传的json：");
    Serial.println(jsonBuf);

    // 上传数据
    boolean d = client.publish( ALINK_TOPIC_PROP_POST, jsonBuf);
    if(d==1){
      Serial.println("[info] 发送成功");
    }else{
      Serial.println("[info] 发送失败"); 
    }
}
